﻿// ------------------------------------------------------------------------
// 版权信息
// 版权归重庆虫儿飞科技有限公司所有。
// 所有权利保留。
// 官方网站：https://netokit.com
// 许可证信息
// Neto.Kit 项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
// 
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
// 
// 其他重要信息
// Neto.Kit 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Neto.Kit 项目的其他详细信息，请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
// 
// 更多信息
// 请访问 https://netokit.com 获取更多关于 Neto.Kit 项目的许可证和版权信息。
// ------------------------------------------------------------------------

#region

using Neto.Application.Api;
using Neto.Db.SqlSugar;
using Neto.Web;
using Yitter.IdGenerator;
using static Neto.Core.Utils.Extensions.MenuExtensions;

#endregion

namespace Neto.Application;

/// <summary>
///     菜单服务
/// </summary>
/// <param name="db"></param>
[ApiDescriptionSettings("Api", Name = "ApiMenuService")]
[PgApiController("菜单服务", MenuPCode = "Sys_Manage")]
[PgApiMenu("菜单管理", "manage_menu", MenuPCode = "Sys_Manage", Icon = "material-symbols:route", Sort = 3)]
[ApiArea]
public class ApiMenuService(
    ISqlSugarClient db)
    : IDynamicApiController
{
    /// <summary>
    ///     获取所有页面名称
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    [OperationId(nameof(GetAllPages))]
    [PgApiAction("获取所有页面名称", Action = "GetAllPages", Sort = 1)]
    public async Task<List<NKeyValue<long, string>>> GetAllPages()
    {
        var result = await db.Queryable<SysMenu>().Where(it => it.Area == "Api" && it.PgType == PgType.Menu)
            .OrderBy(it => new { IsHomePage = SqlFunc.Desc(it.IsHomePage), it.Sort })
            .Select(it => new NKeyValue<long, string> { Key = it.Id, Value = it.Name })
            .ToListAsync();

        return result;
    }

    /// <summary>
    ///     删除菜单
    /// </summary>
    /// <param name="ids"></param>
    /// <returns></returns>
    [HttpPost]
    [OperationId(nameof(Delete))]
    [PgApiAction("删除菜单", Action = "Delete", Sort = 2)]
    public async Task<bool> Delete(string ids)
    {
        return await db.FakeDeleteAsync<SysMenu>(ids);
    }

    /// <summary>
    ///     添加菜单
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    [OperationId(nameof(Insert))]
    [PgApiAction("添加菜单", Action = "Insert", Sort = 3)]
    public async Task<long> Insert(ApiMenuInput input)
    {
        //判断前段key是否存在
        if (db.Queryable<SysMenu>().Where(it => it.Code == input.Code).Any()) throw Oops.Oh("菜单代码重复");

        if (db.Queryable<SysMenu>().Where(it => it.UrlKey == input.UrlKey).Any()) throw Oops.Oh("前端Key重复");
        var sysMenu = input.Adapt<SysMenu>();
        sysMenu.Id = YitIdHelper.NextId();


        return await db.Insertable(sysMenu).ExecuteCommandAsync();
    }

    /// <summary>
    ///     修改菜单
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    [OperationId(nameof(Update))]
    [PgApiAction("修改菜单", Action = "Update", Sort = 4)]
    public async Task<bool> Update(ApiMenuInput input)
    {
        if (input == null || input.Id == null || input.Id.Value == 0) throw Oops.Oh("参数错误");

        //判断前段key是否存在
        if (db.Queryable<SysMenu>().Where(it => it.Code == input.Code && it.Id != input.Id).Any())
            throw Oops.Oh("菜单代码重复");

        if (db.Queryable<SysMenu>().Where(it => it.UrlKey == input.UrlKey && it.Id != input.Id).Any())
            throw Oops.Oh("前端Key重复");

        //根据Id查询
        var dbEntity = await db.Queryable<SysMenu>().Where(it => it.Id == input.Id)
            .FirstAsync();
        if (dbEntity == null) throw Oops.Oh("修改失败");

        dbEntity = input.Adapt<SysMenu>();


        return db.Updateable(dbEntity).ExecuteCommandHasChange();
    }


    /// <summary>
    ///     获取权限树形结构
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    [OperationId(nameof(GetActionTreeList))]
    [PgApiAction("获取权限树形结构", Action = "GetActionTreeList", Sort = 5)]
    public async Task<List<ApiMenuItem>> GetActionTreeList(ApiMenuQuery query)
    {
        var root = "Api";
        var queryable = db.Queryable<SysMenu>()
                .Where(it =>
                    it.PgType == PgType.Action || it.PgType == PgType.Controller || it.PgType == PgType.Directory)
            ;


        if (query.StartTime.HasValue) queryable = queryable.Where(it => it.CreationTime >= query.StartTime.Value);
        if (query.EndTime.HasValue) queryable = queryable.Where(it => it.CreationTime <= query.EndTime.Value);

        if (!string.IsNullOrWhiteSpace(query.Keywords))
        {
            var kw = query.Keywords.ToLower();
            queryable = queryable.Where(it =>
                it.Name.ToLower().Contains(kw) ||
                it.Code.ToLower().Contains(kw) ||
                it.MenuPCode.ToLower().Contains(kw) ||
                it.Route.ToLower().Contains(kw) ||
                it.UrlKey.ToLower().Contains(kw) ||
                it.Url.ToLower().Contains(kw) ||
                it.Area.ToLower().Contains(kw)
            );
        }

        var result = await queryable
                .Where(it=>it.IsLoginOnly==false)
                .Select(it => new ApiMenuItem(), true)
                .Mapper(it =>
                {
                    it.PgTypeStr = it.PgType.ToString("G");
                })
                .OrderBy(it => it.Sort)
                .ToTreeAsync(it => it.Children, it => it.MenuPCode, root, it => it.Code)
            ;


        return result;
    }

    /// <summary>
    ///     获取菜单树形结构
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    [OperationId(nameof(GetMenuTreeList))]
    [PgApiAction("获取菜单树形结构", Action = "GetMenuTreeList", Sort = 6)]
    public async Task<List<ApiMenuItem>> GetMenuTreeList(ApiMenuQuery query)
    {
        var root = "Api";
        var queryable = db.Queryable<SysMenu>()
            .Where(it => it.PgType == PgType.Menu || it.PgType == PgType.Directory)
            .Select(it => new ApiMenuItem(), true);

        if (!string.IsNullOrWhiteSpace(query.Keywords))
        {
            var kw = query.Keywords.ToLower();
            queryable = queryable.Where(it =>
                it.Name.ToLower().Contains(kw) ||
                it.Code.ToLower().Contains(kw) ||
                it.MenuPCode.ToLower().Contains(kw) ||
                it.Route.ToLower().Contains(kw) ||
                it.UrlKey.ToLower().Contains(kw) ||
                it.Url.ToLower().Contains(kw) ||
                it.Area.ToLower().Contains(kw)
            );
        }

        var result = await queryable
                .Mapper(it =>
                {
                    it.PgTypeStr = it.PgType.ToString("G");
                })
                .OrderBy(it => it.Sort)
                .ToTreeAsync(it => it.Children, it => it.MenuPCode, root, it => it.Code)
            ;


        return result;
    }


    /// <summary>
    ///     获取菜单以及方法树结构
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    [OperationId(nameof(GetMenuWithActionTreeList))]
    [PgApiAction("获取菜单以及方法树结构", Action = "GetMenuWithActionTreeList", Sort = 7)]
    [Obsolete]
    public async Task<List<ApiMenuItem>> GetMenuWithActionTreeList(ApiMenuQuery query)
    {
        var root = "Api";
        var queryable = db.Queryable<SysMenu>()
            //.Where(it => it.PgType == PgType.Menu || it.PgType == PgType.Directory)
            ;

        if (!string.IsNullOrWhiteSpace(query.Keywords))
        {
            var kw = query.Keywords.ToLower();
            queryable = queryable.Where(it =>
                it.Name.ToLower().Contains(kw) ||
                it.Code.ToLower().Contains(kw) ||
                it.MenuPCode.ToLower().Contains(kw) ||
                it.Route.ToLower().Contains(kw) ||
                it.UrlKey.ToLower().Contains(kw) ||
                it.Url.ToLower().Contains(kw) ||
                it.Area.ToLower().Contains(kw)
            );
        }

        var result = await queryable
            .OrderBy(it => it.Sort)
            .ToListAsync();
        ;


        var menus = GetMenusWithAction(result, root, root);
        return menus;
    }

    /// <summary>
    ///     获取角色 权限 Dtree 树
    /// </summary>
    /// <returns></returns>
    [AllowAnonymous]
    [HttpPost]
    public async Task<List<ApiMenuItem>> GetRoleDtreeCheck(ApiMenuQuery query)
    {
        var root = "Api";
        var menus = await db.Queryable<SysMenu>().Where(o => o.Area != null && o.Area.ToLower() == root.ToLower())
            .ToListAsync();
        //只查询显示可用且可显示的菜单
        menus = menus.Where(o => !(o.PgType == PgType.Action && o.IsMenu)).ToList();


        var roleMenus = new List<SysMenu>();


        var alldatas = menus
            .Where(o => o.Area == root)
            .Select(o => new ApiMenuItem())
            .ToList();
        var selectdatas =
            roleMenus.Select(o => new ApiMenuItem());

        return null;
    }

    /// <summary>
    ///     获取后台菜单
    /// </summary>
    /// <param name="menus"></param>
    /// <param name="pid">父级ID</param>
    /// <param name="area">区域</param>
    /// <returns></returns>
    [Obsolete]
    private List<ApiMenuItem> GetMenusWithAction(List<SysMenu> menus, string pid = "", string area = "")
    {
        var menuItems = new List<ApiMenuItem>();
        foreach (var menu in menus.Where(o => o.MenuPCode != null && o.MenuPCode.ToLower() == pid.ToLower())
                     .OrderBy(o => o.Sort).ToList())
            if (menu.PgType == PgType.Directory) //目录
            {
                var menuItem = menu.Adapt<ApiMenuItem>();

                menuItem.Children = GetMenusWithAction(menus, menu.Code, area);
                menuItems.Add(menuItem);
            }
            else if (menu.PgType == PgType.Menu)
            {
                var menuItem = menu.Adapt<ApiMenuItem>();

                //检查这个菜单下有哪些方法
                if (!string.IsNullOrWhiteSpace(menuItem.ControllerCode))
                    menuItem.Children = GetMenusWithAction(menus, menuItem.ControllerCode, area);

                menuItems.Add(menuItem);
            }
            else if (menu.PgType == PgType.Action)
            {
                var menuItem = menu.Adapt<ApiMenuItem>();

                menuItems.Add(menuItem);
            }


        //排序
        return [.. menuItems.OrderBy(it => it.Sort)];
    }

    /// <summary>
    ///     获取权限路由
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    [OperationId(nameof(GetUserRoutes))]
    [PgApiAction("获取权限路由", Action = "GetUserRoutes", Sort = 8, IsLoginOnly = true)]
    public async Task<ApiUserRouteOut> GetUserRoutes()
    {
        var area = "Api";
        var dbMenus = await db.Queryable<SysMenu>()
            .Where(it => it.PgType == PgType.Menu || it.PgType == PgType.Directory)
            .Where(it => it.IsEnable == true && it.Area == "Api")
            .ToListAsync();


        var loginUserId = App.HttpContext.GetLoginUserId();

        var user = await db.GetFirstAsync<SysUser>(o => o.Id == loginUserId);

        if (user.UserType != UserType.系统维护) dbMenus = dbMenus.Where(o => o.IsDeveloper == false).ToList();
        //如果是系统管理员或开发者，跳过
        if (user != null && user.UserType != UserType.超级管理员 && user.UserType != UserType.系统维护)
        {


            //否则执行过滤权限
            //获取有权限的菜单
            var userMenus = await db.Queryable<SysRoleMenu>()
                .InnerJoin<SysUserRole>((rm, ur) => rm.RoleId == ur.RoleId)
                .InnerJoin<SysMenu>((rm, ur, m) => rm.MenuId == m.Id)
                .Where((rm, ur, m) => ur.UserId == loginUserId)
                .Where((rm, ur, m) => m.PgType == PgType.Menu || m.PgType == PgType.Directory)
                .GroupBy((rm, ur, m) => m.Id)
                .Select((rm, ur, m) => m)
                .ToListAsync();

            foreach (var menuitem in dbMenus.Where(o => o.PgType == PgType.Menu).ToList())
            {

                if (userMenus.All(o => o.Id != menuitem.Id))
                {
                    //判断是不是home,home默认不需要权限就可以看到这个页面
                    if (menuitem.IsHomePage == true)
                    {
                        continue;
                    }

                    // //判断是不是需要权限的菜单
                    // if (menuitem.IsPermission == false)
                    //     //这是不需要权限的菜单
                    //     continue;


                    //未从用户角色菜单中找到
                    var menu = dbMenus.First(o => o.Id == menuitem.Id);
                    //验证是否有下级功能
                    if (userMenus.Any(o => o.MenuPCode == menu.Code))
                        //这是用户拥有权限的菜单
                        continue;

                    //没有下级功能，且没有菜单权限，移除
                    dbMenus.Remove(menu);
                }
            }
        }

        var homePageKey = await db.Queryable<SysRole>()
            .InnerJoin<SysUserRole>((r, ur) => r.Id == ur.RoleId)
            .InnerJoin<SysMenu>((r, ur, m) => r.HomeMenuId == m.Id)
            .Where((r, ur, m) => ur.UserId == loginUserId)
            .Select((r, ur, m) => m.UrlKey)
            .FirstAsync();

        if (string.IsNullOrWhiteSpace(homePageKey))
            //默认首页是home
            homePageKey = "home";

        var menus = dbMenus.Adapt<List<MenuRouteItem>>();
        var rmenus = menus.GetMenus(area, area);

        //遍历，移除没有下级的空目录
        foreach (var item in rmenus.ToList())
        {
            //跳过主菜单，没有上级的菜单
            if (item.PgType == PgType.Menu) continue;

            if (item.Children == null || item.Children.Count == 0) rmenus.Remove(item);
        }

        return new ApiUserRouteOut { Home = homePageKey, Routes = rmenus };
    }


    /// <summary>
    ///     获取非权限路由
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    [OperationId(nameof(GetConstantRoutes))]
    [AllowAnonymous]
    public List<ApiMenuRouteOut> GetConstantRoutes()
    {
        List<ApiMenuRouteOut> result = [];
        result.Add(new ApiMenuRouteOut
        {
            Name = "login",
            Path = "/login/:module(pwd-login)?",
            Component = "layout.blank$view.login",
            //Props = true,
            Meta = new ApiMenuMeta { Title = "login", I18nKey = "route.login", Constant = true, HideInMenu = true }
        });

        result.Add(new ApiMenuRouteOut
        {
            Name = "403",
            Path = "/403",
            Component = "layout.blank$view.403",
            Meta = new ApiMenuMeta { Title = "403", I18nKey = "route.403", Constant = true, HideInMenu = true }
        });
        result.Add(new ApiMenuRouteOut
        {
            Name = "404",
            Path = "/404",
            Component = "layout.blank$view.404",
            Meta = new ApiMenuMeta { Title = "404", I18nKey = "route.404", Constant = true, HideInMenu = true }
        });
        result.Add(new ApiMenuRouteOut
        {
            Name = "500",
            Path = "/500",
            Component = "layout.blank$view.500",
            Meta = new ApiMenuMeta { Title = "500", I18nKey = "route.500", Constant = true, HideInMenu = true }
        });

        //result.Add(new ApiMenuRouteOut()
        //{
        //    Name = "user-center",
        //    Path = "/user-center",
        //    Component = "layout.base$user-center",
        //    Meta = new()
        //    {
        //        Title = "user-center",
        //        I18nKey = "route.user-center",
        //        Constant = true,
        //        HideInMenu = true
        //    }
        //});
        return result;
    }
}